package org.fujay.commons.core.util;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author slm
 * @description 时间戳性能优化
 */
public class SystemClock {

    /**
     * 线程名称
     */
    private static final String THREAD_NAME = "system.clock";
    /**
     * 时间更新的精度，以毫秒为单位
     */
    private final long precision;
    /**
     * 当前时间戳的原子引用
     */
    private final AtomicLong now;
    /**
     * 调度器，用于定期更新时间
     */
    private final ScheduledExecutorService scheduler;

    /**
     * 私有构造函数
     */
    private SystemClock(long precision) {
        this.precision = precision;
        now = new AtomicLong(System.currentTimeMillis());
        scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(runnable, THREAD_NAME);
            /*设置为守护线程*/
            thread.setDaemon(true);
            return thread;
        });
        /*调度定时更新任务*/
        scheduleClockUpdating();
    }

    /**
     * 提供公共方法以获取单例实例
     */
    public static SystemClock millisClock() {
        return Holder.MILLIS_CLOCK;
    }

    /**
     * 安排定时任务以更新当前时间
     */
    private void scheduleClockUpdating() {
        scheduler.scheduleAtFixedRate(() -> {
            try {
                /*安全地更新当前时间*/
                now.set(System.currentTimeMillis());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, precision, precision, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取当前时间戳
     */
    public long now() {
        return now.get();
    }

    /**
     * 获取未来某个时间点的时间戳
     */
    public long future(long offset) {
        return now() + offset;
    }

    /**
     * 关闭调度器，释放资源
     */
    public void shutdown() {
        if (!scheduler.isShutdown()) {
            scheduler.shutdown();
        }
    }

    /**
     * 使用内部静态类实现单例
     */
    private static class Holder {
        /*默认精度为1毫秒的系统时钟单例*/
        static final SystemClock MILLIS_CLOCK = new SystemClock(1);
    }

}
